using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Diagnostics;
using System.Text;
using System.Reflection;
using System.IO;
using System.Xml;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace IWCommunity.TagsProcessor
{
    /// <summary>
    /// This is a command line tool which loops through all content in the site collection so as to create tag files
    /// which are used by web parts on the site. These need to be pre-created because the process is too slow to be done
    /// in code behind a web page.
    /// There are some slight tweaks which need to be done to improve performance
    /// </summary>
    class Program
    {

         static void Main(string[] args)
        {

            try
            {


                string siteUrl = ConfigurationManager.AppSettings["siteUrl"];
                string blogSectionUrl = ConfigurationManager.AppSettings["blogSectionUrl"];
                string tagColumns = ConfigurationManager.AppSettings["tagColumns"];
                if (!(siteUrl != null && siteUrl.Length > 0) || !(blogSectionUrl != null && blogSectionUrl.Length > 0) || !(tagColumns != null && tagColumns.Length > 0))
                {
                    LogMessage("Missing siteUrl, or blogSectionUrl or tagColumnsUrl from app.config, program terminating");
                    Environment.Exit(0);
                }

                //we are going to create 3 files
                string entireSiteTagFile = ConfigurationManager.AppSettings["entireSiteTagFile"];
                string entireSiteTagFileMaxItems = ConfigurationManager.AppSettings["entireSiteTagFileMaxItems"];

                if (!(entireSiteTagFile != null && entireSiteTagFile.Length > 0) || !(entireSiteTagFileMaxItems != null && entireSiteTagFileMaxItems.Length > 0))
                {
                    LogMessage("Missing entireSiteTageFile, or entireSiteTagFileMaxItems from app.config, program terminating");
                    Environment.Exit(0);

                }
                else
                {
                    BuildXMLFile(siteUrl, entireSiteTagFile, Convert.ToInt16(entireSiteTagFileMaxItems), tagColumns);
                }




                string homePageTagFile = ConfigurationManager.AppSettings["homePageTagFile"];
                string homePageTagFileMaxItems = ConfigurationManager.AppSettings["homePageTagFileMaxItems"];

                if (!(homePageTagFile != null && homePageTagFile.Length > 0) || !(homePageTagFileMaxItems != null && homePageTagFileMaxItems.Length > 0))
                {
                    LogMessage("Missing homePageTagFile, or homePageTagFileMaxItems from app.config, program terminating");
                    Environment.Exit(0);

                }
                else
                {
                    BuildXMLFile(siteUrl, homePageTagFile, Convert.ToInt16(homePageTagFileMaxItems), tagColumns);
                }

                string blogSectionTagFile = ConfigurationManager.AppSettings["blogSectionTagFile"];
                string blogSectionTagFileMaxItems = ConfigurationManager.AppSettings["blogSectionTagFileMaxItems"];
                if (!(blogSectionTagFile != null && blogSectionTagFile.Length > 0) || !(blogSectionTagFileMaxItems != null && blogSectionTagFileMaxItems.Length > 0))
                {
                    LogMessage("Missing blogSectionTagFile, or blogSectionTagFileMaxItems from app.config, program terminating");
                    Environment.Exit(0);

                }
                else
                {
                    BuildXMLFile(siteUrl, blogSectionTagFile, Convert.ToInt16(blogSectionTagFileMaxItems), tagColumns);
                }
            }
            catch (Exception ex)
            {
                LogError(ex);

            }
            
       }

        public static void LogError(Exception ex)
        {
            // create error message
            string errorMessage = ex.Message + " " + ex.StackTrace;

            // add any inner exceptions
            Exception innerException = ex.InnerException;
            while (innerException != null)
            {
                errorMessage += "Inner Error: " + innerException.Message + " " + innerException.StackTrace;
                innerException = innerException.InnerException;
            }

            // log error
            LogMessage(errorMessage);

            //TODO: add logic to add to event log
            EventLog evt = new EventLog("Application", ".", "IWCommunity");
            evt.WriteEntry(Assembly.GetExecutingAssembly().FullName + " : " + errorMessage, System.Diagnostics.EventLogEntryType.Error, 10029);
        }

        private static void BuildXMLFile(string startingSiteUrl, string destinationFileUrl,int maxItems, string tagColumns)
        {
            SortedList tagsList = new SortedList();
            

            //open site and open web
            using (SPSite site = new SPSite(startingSiteUrl))
            {
                //loop through all webs in the site collection
                for (int i = 0; i < site.AllWebs.Count; i++)
                {
                    AddTagsToList(site.AllWebs[i], ref tagsList, tagColumns);
                }

                if (tagsList.Count == 0)
                {
                    LogMessage("No tags found for building the file " + destinationFileUrl);
                    return;
                }

                ArrayList tempList = new ArrayList();
                tempList.InsertRange(0, tagsList.Values); //copy tag objects from sorted list to arraylist
                tempList.Sort(); //sort array list, this will sort by count i.e. number of tags of specific type               

                int maxCount = ((Tag)tempList[0]).Count; //get the highest count value
                LogMessage(maxCount + " tags found for the file " + destinationFileUrl);
                int totalTags = tempList.Count;

                if (totalTags < maxItems)
                {
                    maxItems = totalTags;
                }
                else
                {
                    tempList.RemoveRange(maxItems, totalTags - maxItems); //trim down list to only have ones we want based on the maximum items requested
                }

                tempList.Sort(new TagNameSort()); //sort back based on tag name


                XmlDocument oDoc = new XmlDocument();
                oDoc.LoadXml("<rows/>"); //add outer element called rows                 

                for (int i = 0; i < tempList.Count; i++)
                {
                    Tag tag = (Tag)tempList[i];
                    XmlElement rowNode = oDoc.CreateElement("row"); //add inner element called row
                    rowNode.SetAttribute("tagUrl", tag.Url); //add appropriate attrributes
                    rowNode.SetAttribute("title", tag.Count.ToString() + " tags");
                    rowNode.SetAttribute("displayTitle", SPEncode.HtmlEncode(tag.Name));
                    rowNode.SetAttribute("weight", GetTagWeight(tag.Count, maxCount));
                    oDoc.DocumentElement.AppendChild(rowNode);

                }

                MemoryStream memStream = new MemoryStream();
                XmlTextWriter xmlWriter = new XmlTextWriter(memStream,Encoding.UTF8);

                oDoc.WriteTo(xmlWriter);
                xmlWriter.Flush();
                LogMessage("Attempting to save file " + destinationFileUrl);
                site.RootWeb.Files.Add(destinationFileUrl, memStream, true); //upload file to SharePoint and overrite the existing
                LogMessage("File " + destinationFileUrl + " saved succesfully");
                xmlWriter.Close();
                //Console.WriteLine("Finished");
            }



        }


        private static string GetTagWeight(int Count, int MaxCount)
        {
            //MaxCount = 30;
            double Percent = (double)((Count * 100) / MaxCount);

            if (Percent > 90)
            {
                return "1";
            }
            else if (Percent > 70 )
            {
                return "2";
            }
            else if (Percent > 50)
            {
                return "3";
            }
            else if (Percent > 30)
            {
                return "4";
            }
            else if (Percent > 10)
            {
                return "5";
            }
            else
            {
                return "6";
            }

            //to be technically perfect the number ranges would be
            //0-16% = 6
            //17 - 33% = 5
            //34 - 50% = 4
            //51 - 66% = 3
            //67 - 83% = 2
            //84 - 100% = 1
        }

        private static void AddTagsToList(SPWeb web, ref SortedList tagsCollection, string tagColumns)
        {

            TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
            LogMessage("Looking in web called " + web.Title + " for lists with tags");
            //loop through all lists
            for (int i = 0; i < web.Lists.Count; i++)
            {
                SPList list = web.Lists[i]; //get the list  //genericlist, 
                LogMessage("Looking in list called " + list.Title + " for fields with tags");
                string[] tagsColumnsList = tagColumns.Split(";".ToCharArray());
                foreach (string tagColumn in tagsColumnsList)
                {
                    if (list.Fields.ContainsField(tagColumn)) //if the list contains a column with is a "tags" column continue
                    {
                        LogMessage("The list " + list.Title + " contains a field called " + tagColumn + " which will be used for tags");
                        SPListItemCollection items = list.Items; //get a list of items
                        foreach (SPListItem item in items)
                        {
                            string[] itemtags = item.GetFormattedValue(tagColumn).Trim().Split(";".ToCharArray()); //get tag column value from list item and split it using ; if it contains multiple items
                            int tagsCount = itemtags.Length;
                            for (int y = 0; y < tagsCount; y++) //for each tag in the tag column
                            {
                                string itemtag = itemtags[y].Trim();
                                itemtag = ti.ToTitleCase(itemtag); //extract the tag in title case
                                if (!String.IsNullOrEmpty(itemtag))
                                {
                                    if (tagsCollection.ContainsKey(itemtag)) //if hashtable already has item then add it
                                    {
                                        //int index = (int)ht[itemtag];
                                        // update the count for the tag
                                        ((Tag)tagsCollection[itemtag]).Count++;                                      

                                    }
                                    else
                                    {
                                        tagsCollection.Add(itemtag, new Tag(itemtag, 1, string.Format("/search/results.aspx?v1=date&k=tags:%22{0}%22",SPEncode.HtmlEncode(itemtag))));
                                        //ht.Add((string)itemtag, al.Count - 1);
                                    }


                                }//close if

                            }//close for 
                        }//close for each

                    }//close if

                }//close foreach


            }//close for

        }//close function

        private static void LogMessage(string message)
        {
            Console.WriteLine(message);
            //TODO:add logging support
        }
    }//close class
}//close namespace
